﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net;
using System.IO;
using System.Xml;
using System.Xml.Linq;

namespace Twittervice.api
{
    /// <summary>
    /// This class implements the functionality to interact with the Twitter XML API.  This includes
    /// the ability to retrieve different timelines, retrieve the list of followers, retrieve the
    /// list of friends and update the status of a user.
    /// </summary>
    public class TwitterProvider
    {
        // Twitter XML URLS
        private static string XML_FORMAT = ".xml";
        private static string FRIENDS_TIMELINE_URL = "http://twitter.com/statuses/friends_timeline";
        private static string USER_TIMELINE_URL = "http://twitter.com/statuses/user_timeline";
        private static string PUBLIC_TIMELINE_URL = "http://twitter.com/statuses/public_timeline";
        private static string FRIENDS_URL = "http://twitter.com/statuses/friends";
        private static string FOLLOWERS_URL = "http://twitter.com/statuses/followers";
        private static string STATUS_UPDATE_URL = "http://twitter.com/statuses/update";

        // User for this provider
        TwitterUser user;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="theUser">the user for this provider</param>
        public TwitterProvider(TwitterUser theUser)
        {
            System.Net.ServicePointManager.Expect100Continue = false;

            if (theUser == null)
            {
                throw new Exception();
            }

            user = theUser;

            Logger.getInstance().Log(Logger.Level.INFO, 
                "Created TwitterProvider for user: " + user.ScreenName);
        }

        /// <summary>
        /// Retrieve the timeline for the user of this provider.
        /// </summary>
        /// <returns>List of status objects</returns>
        public List<TwitterStatus> GetFriendsTimeLine()
        {
            List<TwitterStatus> statusList = new List<TwitterStatus>();

            try
            {
                // Create request           
                HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(
                    FRIENDS_TIMELINE_URL + XML_FORMAT);
                SetRequestParams(request);

                // Set Request Method 
                request.Method = "GET";

                // Get Response
                WebResponse response = request.GetResponse();
                string responseString = "";
                using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                {
                    responseString = reader.ReadToEnd();
                }

                // Load into an XML Document
                XmlDocument xmld = new XmlDocument();
                xmld.LoadXml(responseString);

                // Retrieve the status elements
                XmlNodeList statusElements = xmld.GetElementsByTagName(TwitterStatus.STATUS_TAG);

                // Create Status List
                foreach (XmlNode n in statusElements)
                {
                    TwitterStatus newStatus = TwitterStatus.parseXml(n.ChildNodes);
                    statusList.Add(newStatus);
                }
            }
            catch (Exception e)
            {
                Logger.getInstance().Log(Logger.Level.SEVERE,
                    this.GetType().ToString() + "GetFriendsTimeLine() Error: " + e);
            }
            return statusList;
        }

        /// <summary>
        /// Retrieves the timeline of the specified user.
        /// </summary>
        /// <param name="username"></param>
        /// <returns>List of status objects</returns>
        public List<TwitterStatus> GetUserTimeLine(string username)
        {
            List<TwitterStatus> statusList = new List<TwitterStatus>();

            try
            {
                // Create request           
                HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(
                    USER_TIMELINE_URL + "/" + username + XML_FORMAT);
                SetRequestParams(request);

                // Set Request Method 
                request.Method = "GET";

                // Get Response
                WebResponse response = request.GetResponse();
                string responseString = "";
                using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                {
                    responseString = reader.ReadToEnd();
                }

                // Load into an XML Document
                XmlDocument xmld = new XmlDocument();
                xmld.LoadXml(responseString);

                // Retrieve the status elements
                XmlNodeList statusElements = xmld.GetElementsByTagName(TwitterStatus.STATUS_TAG);

                // Create Status List
                foreach (XmlNode n in statusElements)
                {
                    TwitterStatus newStatus = TwitterStatus.parseXml(n.ChildNodes);
                    statusList.Add(newStatus);
                }
            }
            catch (Exception e)
            {
                Logger.getInstance().Log(Logger.Level.SEVERE,
                    this.GetType().ToString() + "GetUserTimeLine() Error: " + e);
            }
            return statusList;
        }

        /// <summary>
        /// Gets the public timeline from Twitter.
        /// </summary>
        /// <returns>List of status objects</returns>
        public List<TwitterStatus> GetPublicTimeLine()
        {
            List<TwitterStatus> statusList = new List<TwitterStatus>();

            try
            {
                // Create request           
                HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(
                    PUBLIC_TIMELINE_URL + XML_FORMAT);
                SetRequestParams(request);

                // Set Request Method 
                request.Method = "GET";

                // Get Response
                WebResponse response = request.GetResponse();
                string responseString = "";
                using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                {
                    responseString = reader.ReadToEnd();
                }

                // Load into an XML Document
                XmlDocument xmld = new XmlDocument();
                xmld.LoadXml(responseString);

                // Retrieve the status elements
                XmlNodeList statusElements = xmld.GetElementsByTagName(TwitterStatus.STATUS_TAG);

                // Create Status List
                foreach (XmlNode n in statusElements)
                {
                    TwitterStatus newStatus = TwitterStatus.parseXml(n.ChildNodes);
                    statusList.Add(newStatus);
                }
            }
            catch (Exception e)
            {
                Logger.getInstance().Log(Logger.Level.SEVERE,
                    this.GetType().ToString() + "GetPublicTimeLine() Error: " + e);
            }
            return statusList;
        }

        /// <summary>
        /// Retrieves the list of friends for the specified user.
        /// </summary>
        /// <param name="username"></param>
        /// <returns>List of Twiter Users</returns>
        public List<TwitterUser> getFriendsList(String username)
        {
            return getUserList(FRIENDS_URL, username);
        }

        /// <summary>
        /// Retrieves the list of followers for the specified user.
        /// </summary>
        /// <param name="username"></param>
        /// <returns>List of Twitter Users</returns>
        public List<TwitterUser> getFollowersList(String username)
        {
            return getUserList(FOLLOWERS_URL, username);
        }

        /// <summary>
        /// Updates the Twitter status for the user of this provider.
        /// </summary>
        /// <param name="message">The status message</param>
        /// <returns>The response from Twitter</returns>
        public string UpdateStatus(string message)
        {
            string responseString = "No Response";
            try
            {
                // Create request           
                HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(
                    STATUS_UPDATE_URL + XML_FORMAT);
                SetRequestParams(request);

                // Set Request Method 
                request.Method = "POST";

                // Convert message to bytes
                byte[] messageAsBytes = System.Text.Encoding.ASCII.GetBytes("status=" + message
                    + "&source=Twittervice");
                request.ContentLength = messageAsBytes.Length;

                // Write message
                using( Stream requestStream = request.GetRequestStream() ){
                    requestStream.Write(messageAsBytes, 0, messageAsBytes.Length);
                }

                WebResponse response = request.GetResponse();
                using( StreamReader reader = new StreamReader(response.GetResponseStream()) ){
                    responseString = reader.ReadToEnd();
                }
            }
            catch (Exception e)
            {
                Logger.getInstance().Log(Logger.Level.SEVERE,
                    this.GetType().ToString() + "UpdateStatus() Error: " + e);
            }
            return responseString;
        }

        /// <summary>
        /// The the list of users from the specified Twitter url.  This method is used for retrieving
        /// friends and followers.
        /// </summary>
        /// <param name="twitterUrl"></param>
        /// <param name="username"></param>
        /// <returns>List of Twitter Users.</returns>
        private List<TwitterUser> getUserList(String twitterUrl, String username)
        {
            List<TwitterUser> userList = new List<TwitterUser>();

            try
            {
                // Create request           
                HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(
                    twitterUrl + "/" + username + XML_FORMAT);
                SetRequestParams(request);

                // Set Request Method 
                request.Method = "GET";

                // Get Response
                WebResponse response = request.GetResponse();
                string responseString = "";
                using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                {
                    responseString = reader.ReadToEnd();
                }

                // Load into an XML Document
                XmlDocument xmld = new XmlDocument();
                xmld.LoadXml(responseString);

                // Retrieve the status elements
                XmlNodeList userElements = xmld.GetElementsByTagName(TwitterUser.USER_TAG);

                // Create Status List
                foreach (XmlNode n in userElements)
                {
                    TwitterUser newUser = TwitterUser.parseXml(n.ChildNodes);
                    userList.Add(newUser);
                }
            }
            catch (Exception e)
            {
                Logger.getInstance().Log(Logger.Level.SEVERE,
                    this.GetType().ToString() + "GetUserList() Error: " + e);
            }
            return userList;
        }
 
        /// <summary>
        /// Sets basic parameters for the http request.
        /// </summary>
        /// <param name="request"></param>
        private void SetRequestParams( HttpWebRequest request )
        {
            // Set User/Pass
            request.Credentials = new NetworkCredential(user.ScreenName, user.Password);

            // Set other params
            request.Timeout = 500000;
            request.ContentType = "application/x-www-form-urlencoded";
            request.UserAgent = "Twittervice";
        #if USE_PROXY
            request.Proxy = new WebProxy("http://localhost:8080", false);
        #endif
        }
    }
}
